-- 匹配模块
-- Author: Colocasia
-- Date: 2018-08-08

local skynet = require 'skynet';
local skynet_queue = require 'skynet.queue';
local match_list = require 'match_list';
local class = require 'class';
local handler = require 'handler';
local table = require 'us.table';

-- 常量
local ENUM_SERVER = require 'server_const';
local ENUM_MATCH = require 'match_const';
local ENUM_PLAYER = require 'player_const';

local M = class('match', handler);


local team_map = {};
local match_teams = {};
local match_lists = {};
local queue_cache = {};
---确认选择的列表
local select_lists = {};


local match_count = 0;
local list_count = 0;

---常用函数
---@type fun @获得当前物理机时间
local skynet_time = skynet.time;
---@type fun @遍历表内容
local table_foreach = table.foreach;
---@type fun @合并表内容
local table_merge = table.merge;

---@type string @当前服务器节点
local server_id = skynet.getenv 'server_id';
---@type string @当前服务地址
local service_addr = skynet.self() or 'match';


---构造函数
function M:ctor()
end


---队列执行
local sq = skynet_queue();
local function _queue(f, ...)
    if 'function' == type(f) then return end
    -- local sq = table.remove(queue_cache, 1);
    -- if not sq then
    --     sq 
    -- end

    local ret = sq(f, ...);
    -- table.insert(queue_cache, sq);
    return ret;
end


---获取队伍
function M:get_team(guid)
    return team_map[guid];
end


---获取队伍匹配信息
function M:get_match_info(guid)
    return match_list.get_match_info(self:get_team(guid));
end


---是否本服务的队伍
function M:is_local(team)
    return team.server == server_id;
end


---获取team
function M:get_team_by_info(info)
    local server = info.server;
    local service = info.service;
    local team_guid = info.guid;

    local team;
    if self:is_local(info) then
        -- mark:这里需要处理跨服的队伍
        team = self:get_team(team_guid);
    else
        team = self:get_team(team_guid);
    end
    return team;
end


---检查队伍是否可以报名
function M:check_team(team)
    -- 检查队伍目标
    local team_target = team.target;
    if not team_target then return false end
    if not team_target.type then return false end
    if not team_target.game then return false end

    -- 有一个玩家不合格视为全员不合格
    for _, player in ipairs(team.players) do
        if not player.server or not player.agent or not player.guid then
            return false;
        end

        if not player.score or not player.level then
            return false;
        end

        if not player.state or player.state ~= ENUM_PLAYER.STATE.IDLE then
            return false;
        end
    end
    return true;
end


---添加队伍进match_teams
local function insert_team(team)
    team.update = skynet_time() + ENUM_MATCH.TEAM_TIMEOUT;
    match_teams[team.guid] = team;
    match_count = match_count + (#team.players or 0);
end


---从match_teams中移除队伍
local function remove_team(team)
    match_teams[team.guid] = nil;
    match_count = match_count - (#team.players or 0);
end


---报名
function M:register(team)
    if not self:check_team(team) then return false end
    insert_team(team);
    team_map[team.guid] = team;
end


---取消报名
function M:unregister(team)



    team_map[team.guid] = nil;
    remove_team(team);
end


---检查创建匹配列表
function M:check_create_match_list()
    if list_count > 0 then return end
    if match_count < list_count * 10 then return end
    return self:create_match_list();
end


---创建匹配列表
function M:create_match_list(team)
    _, team = team and _, team or next(match_teams);
    if not team then return end;
    -- 将队伍移除匹配的队伍列表中
    remove_team(team);
    -- 添加新建的匹配列表
    local params = table_merge({
        team.target,
        {
            server = server_id,
            service = service_addr,
        },
    })
    local list = match_list.create(params);
    match_list.add(list, team, 1);
    match_lists[list.guid] = list;
end


---移除匹配列表
function M:remove_match_list(list)
    if not list then return end
    for info in match_list.pop_teams(list) do
        local team = self:get_team_by_info(info);
        if team then
            match_list.exit(team)
            if self:is_local(team) then
                insert_team(team);
            else
                -- mark:需要处理跨服team

            end
        end
    end
    match_lists[list.guid] = nil;
end


---检查匹配列表
function M:check_match_list(list)
    if not match_list.is_full(list) then return end

    -- 从正在匹配的列表中移除
    match_lists[list.guid] = nil;
    -- 添加到选择列表
    select_lists[list.guid] = list;

    for info in match_list.pop_teams(list) do
        local team = self:get_team_by_info(info);
        table_foreach(team.players, function (player, idx)
            -- 为每个玩家发送确认信息

            
            
        end)
    end



end


---确认匹配列表
function M:select_enter(list_guid, team_guid, player_guid, join)
    local list = select_lists[list_guid];
    if not list then return end
    

    
    self:match_success(list);
end


---匹配成功
function M:match_success(list)
    

    --- mark: 需要申请房间 开始游戏

end


---匹配一个列表
local function _match(list)
    local now = skynet_time();
    table_foreach(match_teams, function(team, guid)
        if now > team.update then
            M:create_match_list(team);
        else
            if match_list.try(list, team) then
                remove_team(team);
                M:check_match_list(list);
            end
        end
    end)
    return list;
end
---匹配一个列表
function M:match(list)
    return _queue(_match, list);
end


---检查匹配
function M:match_all()
    local now = skynet_time();
    table_foreach(match_lists, function(list, guid)
        -- 检查队列是否超时
        if now > list.update then
            return self:remove_match_list(list);
        end

        -- 优先在本服进行匹配
        self:match(list);
        
        -- 跨服匹配
        local server_list = self:exc_server_cmd(_, "get_server_list");
        table_foreach(server_list, function(server)
            if match_list.is_full(list) then return end
            if ENUM_SERVER.STATE.OFFLINE == server.state then return end
            self:exc_match_cmd(server, "match", list);
        end)
    end)

    -- 检查是否创建匹配队列
    self:check_create_match_list()
end


skynet.fork(function()
    while true do
        M:match_all();
        skynet.sleep(ENUM_MATCH.MATCH_INTERVAL);
    end
end)


return M;
